//*******************************************************************************
// COPYRIGHT NOTES
// ---------------
// This is a sample for BCGControlBar Library Professional Edition
// Copyright (C) 1998-2016 BCGSoft Ltd.
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions 
// of the accompanying license agreement.
//*******************************************************************************
//
// ChartStockView.cpp : implementation file
//

#include "stdafx.h"
#include "bcgpchartexample.h"
#include "ChartStockView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CBCGPChartValue CALLBACK ChartStockCallbackFunc(int nDataPointIndex, CBCGPChartStockSeries* pSeries)
{
	CBCGPChartStockData data = pSeries->GetStockDataAt(nDataPointIndex);
	if (data.IsEmpty())
	{
		return CBCGPChartValue();
	}
	return CBCGPChartValue((data.m_dblHigh + data.m_dblLow) / 2);
}

/////////////////////////////////////////////////////////////////////////////
// CChartStockView

IMPLEMENT_DYNCREATE(CChartStockView, CBCGPChartExampleView)

CChartStockView::CChartStockView()
	: CBCGPChartExampleView(CChartStockView::IDD)
{
	//{{AFX_DATA_INIT(CChartStockView)
	m_nLineType = 0;
	//}}AFX_DATA_INIT
}

CChartStockView::~CChartStockView()
{
}

void CChartStockView::DoDataExchange(CDataExchange* pDX)
{
	CBCGPChartExampleView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CChartStockView)
	DDX_Control(pDX, IDC_CHART, m_wndChart);
	DDX_CBIndex(pDX, IDC_LINE_TYPE, m_nLineType);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CChartStockView, CBCGPChartExampleView)
	//{{AFX_MSG_MAP(CChartStockView)
	ON_CBN_SELENDOK(IDC_LINE_TYPE, OnUpdateChart)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChartStockView diagnostics

#ifdef _DEBUG
void CChartStockView::AssertValid() const
{
	CBCGPChartExampleView::AssertValid();
}

void CChartStockView::Dump(CDumpContext& dc) const
{
	CBCGPChartExampleView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CChartStockView message handlers

void CChartStockView::OnInitialUpdate() 
{
	CBCGPChartExampleView::OnInitialUpdate();
	
	if (m_bIsReady)
	{
		return;
	}

	m_bIsReady = TRUE;

	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	m_wndChart.EnableTooltip(TRUE);

	static const CString strCurrencyNames = _T("CUR1/CUR2");

	CBCGPChartStockSeries* pStockSeries = new CBCGPChartStockSeries(pChart, CBCGPBaseChartStockSeries::SST_BAR);
	ASSERT_VALID(pStockSeries);

	pChart->AddSeries(pStockSeries);

	pStockSeries->m_strSeriesName = strCurrencyNames;
	CBCGPChartAxis* pYAxis = pChart->GetChartAxis(BCGP_CHART_Y_PRIMARY_AXIS);
	ASSERT_VALID(pYAxis);

	pYAxis->m_strAxisName = strCurrencyNames;
	pYAxis->m_bDisplayAxisName = TRUE;	
	pYAxis->m_strDataFormat = _T("%.4f");

	CBCGPChartAxis* pXAxis = pChart->GetChartAxis(BCGP_CHART_X_PRIMARY_AXIS);
	ASSERT_VALID(pXAxis);

	pXAxis->m_strAxisName = _T("Date/Time");
	pXAxis->m_bFormatAsDate = TRUE;
	pXAxis->m_strDataFormat = _T("%H:%M");
	pXAxis->EnableMajorUnitIntervalInterlacing(FALSE);
	pXAxis->ShowScrollBar();

	COleDateTime dtStartRange; 
	COleDateTime dtEndRange;
	
	LoadStockData(pStockSeries, dtStartRange, dtEndRange);

	// Set callback function to calculate custom values for line representation
	pStockSeries->SetCustomStockValueCallback(ChartStockCallbackFunc);

	CBCGPChartHistogramSeries* pVolumeSeries = new CBCGPChartHistogramSeries(pChart, _T("Volume"));

	// generate sample data for volumes
	for (int i = 0; i < pStockSeries->GetDataPointCount(); i++)
	{
		pVolumeSeries->AddDataPoint(Rand(700000, 90000000), 
				pStockSeries->GetDataPointValue(i, CBCGPChartData::CI_X));
	}

	pChart->AddSeries(pVolumeSeries);
	pVolumeSeries->m_bIncludeSeriesToLegend = FALSE;

	int nMAPeriod = 14;
	int nRSIPeriod = 9;

	// setup moving averages
	CBCGPChartSeries* pCloseSeries = pStockSeries->GetChildSeries(CBCGPChartStockSeries::CHART_STOCK_SERIES_CLOSE_IDX);

// 	CBCGPChartMAFormula simple(CBCGPChartMAFormula::MA_SIMPLE, nMAPeriod);
// 	simple.Create(pChart, _T("SMA"), pCloseSeries);

	CBCGPChartMAFormula smoothed(CBCGPChartMAFormula::MA_SMOOTHED, nMAPeriod);
	smoothed.Create(pChart, _T("Smooth MA"), pCloseSeries);

	CBCGPChartMAFormula exponential(CBCGPChartMAFormula::MA_EXPONENTIAL, nMAPeriod);
	exponential.Create(pChart, _T("EMA"), pCloseSeries);

// 	CBCGPChartMAFormula lwa(CBCGPChartMAFormula::MA_LINEAR_WEIGHTED, nMAPeriod);
// 	lwa.Create(pChart, _T("LWMA"), pCloseSeries);

	// setup Bollinger Bands
	CBCGPChartBollingerBandsFormula bb(2, 20);
	CBCGPChartSeries* pBollingerSeries = bb.Create(pChart, _T("Bollinger Bands"), pCloseSeries);
	pBollingerSeries->m_bIncludeSeriesToLegend = FALSE;
	pBollingerSeries->SetSeriesLineDashStyle(CBCGPStrokeStyle::BCGP_DASH_STYLE_DASH_DOT);
	
	// setup secondary main Y axis
	pChart->ShowAxis(BCGP_CHART_Y_SECONDARY_AXIS, TRUE, TRUE);
	CBCGPChartAxis* pYSecondaryAxis = pChart->GetChartAxis(BCGP_CHART_Y_SECONDARY_AXIS);
	pYSecondaryAxis->m_strAxisName = strCurrencyNames;

	// Setup custom axis for volumes:
	CBCGPChartAxis* pAxisVolume = pYAxis->Split(60, 6, NULL, FALSE);
	pAxisVolume->SetDisplayUnits(1000000); // displays volume in millions
	pAxisVolume->m_strDataFormat = _T("%.fM"); // ... with postfix M
	pAxisVolume->m_crossType = CBCGPChartAxis::CT_MAXIMUM_AXIS_VALUE; // at right side
	pAxisVolume->m_majorTickMarkType = CBCGPChartAxis::TMT_INSIDE; // in this case "inside" points tick marks outside
	pAxisVolume->m_strAxisName = _T("Volume Axis");

	pVolumeSeries->SetRelatedAxis(pAxisVolume, CBCGPChartSeries::AI_Y);

	// Setup Custom Axes for indicators:
	// RSI
	CBCGPChartAxis* pAxisRSI = (CBCGPChartAxisY*) pYAxis->Split(20, 6, NULL, TRUE);
	pAxisRSI->SetFixedDisplayRange(0, 100, 50);
	pAxisRSI->m_strAxisName = _T("RSI Axis");

	// Stochastic
	CBCGPChartAxis* pAxisStochastic = (CBCGPChartAxisY*) pAxisVolume->Split(30, 6, NULL, FALSE);
	pAxisStochastic->SetFixedDisplayRange(0, 100, 50);
	pAxisStochastic->m_strAxisName = _T("Stochastic Axis");

	// MACD
	CBCGPChartAxis* pAxisMACD = pAxisStochastic->Split(50, 6, NULL, FALSE);
	pAxisMACD->m_strAxisName = _T("MACD Axis");

	// Create indicators
	// RSI
	CBCGPChartRSIFormula rsi(CBCGPChartRSIFormula::RSI_SMOOTHED, nRSIPeriod);
	CBCGPChartSeries* pRSISeries = rsi.Create(pChart, _T("RSI Smooth"), pCloseSeries, pAxisRSI);
	pRSISeries->m_bIncludeSeriesToLegend = FALSE;

//  Uncomment to see exponential and simple types of RSI
// 	CBCGPChartRSIFormula rsi1(CBCGPChartRSIFormula::RSI_EXPONENTIAL, nRSIPeriod);
// 	CBCGPChartSeries* pRSISeries1 = rsi1.Create(pChart, _T("RSI Exp"), pCloseSeries, pAxisRSI);
// 	pRSISeries1->m_bIncludeSeriesToLegend = FALSE;
// 
// 	CBCGPChartRSIFormula rsi2(CBCGPChartRSIFormula::RSI_SIMPLE, nRSIPeriod);
// 	CBCGPChartSeries* pRSISeries2 = rsi2.Create(pChart, _T("RSI Simple"), pCloseSeries, pAxisRSI);
// 	pRSISeries2->m_bIncludeSeriesToLegend = FALSE;

	// Stochastic
	// %K
	CBCGPChartStochasticFormula stochastic(5, 3);
	CBCGPChartSeries* pStochSeries = stochastic.Create(pChart, _T("Stochastic"), pStockSeries, pAxisStochastic);
	pStochSeries->m_bIncludeSeriesToLegend = FALSE;

	// %D
	CBCGPChartMAFormula signalStoch(CBCGPChartMAFormula::MA_SIMPLE, 3);
	CBCGPChartSeries* pSignalStochSeries = signalStoch.Create(pChart, _T("Stochastic Signal"), pStochSeries, pAxisStochastic);
	pSignalStochSeries->m_bIncludeSeriesToLegend = FALSE;
	pSignalStochSeries->SetSeriesLineDashStyle(CBCGPStrokeStyle::BCGP_DASH_STYLE_DASH);
	pSignalStochSeries->SetLongDataOffset(9);

	// MACD
	CBCGPChartMACDFormula macd(12, 26);
	CBCGPChartSeries* pMACDSeries = macd.Create(pChart, _T("MACD"), pCloseSeries, pAxisMACD);
	pMACDSeries->m_bIncludeSeriesToLegend = FALSE;

	// MACD Signal
	CBCGPChartMAFormula signalMACD(CBCGPChartMAFormula::MA_SIMPLE, 9);
	CBCGPChartSeries* pSignalMACDSeries = signalMACD.Create(pChart, _T("Signal MACD"), pMACDSeries, pAxisMACD);
	pSignalMACDSeries->m_bIncludeSeriesToLegend = FALSE;
	pSignalMACDSeries->SetSeriesLineColor(CBCGPBrush(CBCGPColor::DarkRed));

	// X Axis should be always at the diagram bottom along with its labels
	pXAxis->m_crossType = CBCGPChartAxis::CT_FIXED_DEFAULT_POS;
	pXAxis->m_axisLabelType = CBCGPChartAxis::ALT_LOW;
	pChart->EnableResizeAxes(TRUE);

	// Setup overbought and oversold indicator lines and axis markers
	static double dblRSIOversoldValue = 30;
	static double dblRSIOverboughtValue = 70;
	
	SetupIndicatorObjects(dblRSIOversoldValue, pAxisRSI);
	SetupIndicatorObjects(dblRSIOverboughtValue, pAxisRSI, _T("RSI(9)"));

	static double dblStochOversoldValue = 20;
	static double dblStochOverboughtValue = 80;
	SetupIndicatorObjects(dblStochOversoldValue, pAxisStochastic);
	SetupIndicatorObjects(dblStochOverboughtValue, pAxisStochastic, _T("Stochastic(5, 3, 3)"));

	// Setup volume chart
	SetupIndicatorObjects(DBL_MAX, pAxisVolume, _T("Volume"));

	// Setup MACD chart
	SetupIndicatorObjects(DBL_MAX, pAxisMACD, _T("MACD (12, 26, 9)"));

	OnUpdateChart();
}

void CChartStockView::SetupIndicatorObjects(double dblValue, CBCGPChartAxis* pIndicatorAxis, const CString& strIndicatorName)
{
	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	CBCGPChartAxis* pXAxis = pChart->GetChartAxis(BCGP_CHART_X_PRIMARY_AXIS);
	ASSERT_VALID(pXAxis);

	// set overbought/oversold line and its axis mark
	if (dblValue != DBL_MAX)
	{
		const CBCGPChartTheme& colors = pChart->GetColors();
		
		CArray<float, float> dashes;
		dashes.Add(4.0);
		dashes.Add(4.0);
		
		CBCGPStrokeStyle style(dashes);
		
		CBCGPChartLineObject* pLine = 
			pChart->AddChartLineObject(dblValue, TRUE, colors.m_brAxisMajorGridLineColor, 1.0, &style);
		pLine->SetRelatedAxes(pXAxis, pIndicatorAxis);
		pLine->SetForeground(FALSE);

		CString strText;
		strText.Format(_T("%.0f"), dblValue);
		
		CBCGPChartAxisMarkObject* pMark = pChart->AddChartAxisMarkObject(dblValue, strText);
		pMark->SetRelatedAxes(pXAxis, pIndicatorAxis);
	}

	// set indicator name
	if (!strIndicatorName.IsEmpty())
	{
		CBCGPRect rectLabel(5, -3, CBCGPChartObject::_EmptyCoordinate, CBCGPChartObject::_EmptyCoordinate);
		CBCGPChartObject* pTextObject = pChart->AddChartTextObject(rectLabel, strIndicatorName);
		
		pTextObject->SetRelatedAxes(pXAxis, pIndicatorAxis);
	}
}

void CChartStockView::LoadStockData(CBCGPChartStockSeries* pStockSeries, COleDateTime& dtStartRange, COleDateTime& dtEndRange)
{
	COleDateTime dtCurr = COleDateTime::GetCurrentTime();
	COleDateTime dtStart(dtCurr.GetYear(), dtCurr.GetMonth(), max (1, dtCurr.GetDay() - 1), dtCurr.GetHour(), 0, 0 );

	dtStartRange = dtStart;
	
	COleDateTimeSpan span(0, 0, 5, 0);

	pStockSeries->AddData(1.1825, 1.1827, 1.1816, 1.1823, dtStart += span);
	pStockSeries->AddData(1.1824, 1.1829, 1.1823, 1.1826, dtStart += span);
	pStockSeries->AddData(1.1826, 1.1835, 1.1824, 1.1827, dtStart += span);
	pStockSeries->AddData(1.1829, 1.1837, 1.1828, 1.1834, dtStart += span);
	pStockSeries->AddData(1.1836, 1.1840, 1.1835, 1.1838, dtStart += span);
	pStockSeries->AddData(1.1838, 1.1841, 1.1837, 1.1839, dtStart += span);
	pStockSeries->AddData(1.1839, 1.1840, 1.1838, 1.1839, dtStart += span);
	pStockSeries->AddData(1.1839, 1.1840, 1.1836, 1.1837, dtStart += span);
	pStockSeries->AddData(1.1835, 1.1836, 1.1831, 1.1832, dtStart += span);
	pStockSeries->AddData(1.1833, 1.1837, 1.1831, 1.1832, dtStart += span);
	pStockSeries->AddData(1.1832, 1.1835, 1.1831, 1.1833, dtStart += span);
	pStockSeries->AddData(1.1833, 1.1838, 1.1832, 1.1836, dtStart += span);
	pStockSeries->AddData(1.1837, 1.1840, 1.1836, 1.1839, dtStart += span);
	pStockSeries->AddData(1.1839, 1.1844, 1.1839, 1.1842, dtStart += span);
	pStockSeries->AddData(1.1843, 1.1848, 1.1843, 1.1847, dtStart += span);
	pStockSeries->AddData(1.1847, 1.1851, 1.1845, 1.1849, dtStart += span);
	pStockSeries->AddData(1.1849, 1.1858, 1.1847, 1.1855, dtStart += span);
	pStockSeries->AddData(1.1855, 1.1856, 1.1854, 1.1855, dtStart += span);
	pStockSeries->AddData(1.1854, 1.1855, 1.1850, 1.1851, dtStart += span);
	pStockSeries->AddData(1.1849, 1.1850, 1.1842, 1.1843, dtStart += span);
	pStockSeries->AddData(1.1843, 1.1844, 1.1835, 1.1837, dtStart += span);
	pStockSeries->AddData(1.1837, 1.1838, 1.1836, 1.1837, dtStart += span);
	pStockSeries->AddData(1.1838, 1.1843, 1.1836, 1.1840, dtStart += span);
	pStockSeries->AddData(1.1841, 1.1845, 1.1839, 1.1842, dtStart += span);
	pStockSeries->AddData(1.1844, 1.1850, 1.1844, 1.1849, dtStart += span);
	pStockSeries->AddData(1.1848, 1.1853, 1.1847, 1.1851, dtStart += span);
	pStockSeries->AddData(1.1851, 1.1854, 1.1850, 1.1852, dtStart += span);
	pStockSeries->AddData(1.1852, 1.1860, 1.1851, 1.1858, dtStart += span);
	pStockSeries->AddData(1.1858, 1.1866, 1.1857, 1.1865, dtStart += span);
	pStockSeries->AddData(1.1865, 1.1866, 1.1862, 1.1863, dtStart += span);
	pStockSeries->AddData(1.1863, 1.1864, 1.1852, 1.1853, dtStart += span);
	pStockSeries->AddData(1.1853, 1.1855, 1.1849, 1.1850, dtStart += span);
	pStockSeries->AddData(1.1850, 1.1852, 1.1839, 1.1841, dtStart += span);
	pStockSeries->AddData(1.1836, 1.1841, 1.1827, 1.1829, dtStart += span);
	pStockSeries->AddData(1.1825, 1.1827, 1.1819, 1.1820, dtStart += span);
	pStockSeries->AddData(1.1820, 1.1822, 1.1818, 1.1819, dtStart += span);
	pStockSeries->AddData(1.1820, 1.1824, 1.1819, 1.1823, dtStart += span);
	pStockSeries->AddData(1.1823, 1.1825, 1.1822, 1.1824, dtStart += span);
	pStockSeries->AddData(1.1824, 1.1827, 1.1823, 1.1824, dtStart += span);
	pStockSeries->AddData(1.1823, 1.1824, 1.1812, 1.1814, dtStart += span);

	dtEndRange = dtStart + span;
}

void CChartStockView::OnUpdateChart() 
{
	UpdateData();

	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);
	
	CBCGPChartStockSeries* pStockSeries = DYNAMIC_DOWNCAST(CBCGPChartStockSeries, pChart->GetSeries(0));

	if (pStockSeries != NULL)
	{
		pStockSeries->SetStockSeriesType((CBCGPBaseChartStockSeries::StockSeriesType)m_nLineType, FALSE);
		
		if (m_nLineType != 1)
		{
			pStockSeries->SetSeriesLineWidth(2.);
			pStockSeries->m_downBarStyle.m_dblWidth = 2.;
		}
		else
		{
			pStockSeries->SetSeriesLineWidth(1.);
			pStockSeries->m_downBarStyle.m_dblWidth = 1.;
		}
	}

	OnChartAnimation();
}
